<?php


namespace app\aliyun\controller;

use Exception;
use Green\Request\Extension\ClientUploader;
use Green\Request\V20180509\ImageSyncScanRequest;
use Green\Request\V20180509\TextScanRequest;
use Green\Request\V20180509\VoiceAsyncScanRequest;
use Green\Request\V20180509\VoiceAsyncScanResultsRequest;
use think\Log;

class AliyunFilterWord
{
    /**
     * 违规标签
     * @var array
     */
    protected static $illegal_label = [
        'normal' => '正常⽂本',//正常文本
        'spam' => '含垃圾信息',//含垃圾信息
        'ad' => '广告',//广告
        'politics' => '涉政',//涉政
        'terrorism' => '暴恐',//暴恐
        'abuse' => '辱骂',//辱骂
        'porn' => '色情',//色情
        'flood' => '灌水',//灌水
        'contraband' => '违禁',//违禁
        'meaningless' => '无意义',//无意义
        'customized' => '官方关键词',//自定义（例如命中自定义关键词）,
        'bloody' => '血腥',
        'explosion' => '爆炸烟光',
        'outfit' => '特殊装束',
        'logo' => '特殊标识',
        'weapon' => '武器',
        'violence' => '打斗',
        'crowd' => '聚众',
        'parade' => '游行',
        'carcrash' => '车祸现场',
        'flag' => '旗帜',
        'location' => '地标',
        'drug' => '涉毒',
        'gamble' => '赌博',
        'others' => '其他',
        'npx' => '牛皮癣广告',
        'qrcode' => '含二维码',
        'programCode' => '含小程序码',
        'sexy' => '性感',
        'PIP' => '画中画',
        'smoking' => '吸烟',
        'drivelive' => '车内直播',
        'TV' => '含受管控的logo',
        'trademark' => '含商标',
        'ocr' => '图片中包含文字信息。',
        'harmful' => '不良场景',//（保护未成年人场景，支持拜金炫富、追星应援、负面情绪、负面诱导等检测场景）
    ];
    /**
     * 违规等级
     * @var array
     */
    protected static $illegal_level = [
//        'pass',//文本正常
        'review',//需要人工审核
        'block',//文本违规，可以直接删除或者做限制处理
    ];

    /**
     * 文本内容检测
     * @param string $content 需要检测的内容
     * @param string $bizType 业务场景阿里设置
     * @param array $scenes 场景参数
     * @document 接口文档 https://help.aliyun.com/document_detail/70439.html?spm=a2c4g.11186623.6.675.2a2c3db31U0Bee
     * @document 文本库文档 https://help.aliyun.com/document_detail/66057.htm?spm=a2c4g.11186623.2.6.1fce54ebEcUnKO#section-gda-mbh-xld
     * @return array
     */
    public static function checkText(string $content, string $bizType = '', array $scenes = ['antispam'])
    {
        /**
         * 组合检测的数据
         */
        $contents[] = [
            'dataId' => uniqid('', false),//数据Id。需要保证在一次请求中所有的Id不重复。
            'content' => $content//待检测文本
        ];

        //获取文本内容违规比例
        $range = config('app.illegal_range');
        $client = AliyunClient::getInstance()->getClient();
        $request = new TextScanRequest();
        $request->setMethod('POST');
        $request->setAcceptFormat('JSON');

        /**
         * 文本垃圾检测： antispam
         **/
        $param = [
            'tasks' => $contents,
            'scenes' => $scenes
        ];
        if ($bizType)
        {
            $param['bizType'] = $bizType;
        }
        $res = [];
        $request->setContent(json_encode($param, true));
        try
        {
            //返回数据
            $response = $client->getAcsResponse($request);
            $response = json_decode(json_encode($response), true);
//            Log::write('内容检测返回数据:' . json_encode($response['data'], true), 'log', true);
            //若返回code为200
            if (200 === (int)$response['code'])
            {
                //检测结果信息
                $taskResults = $response['data'];
                foreach ($taskResults as $taskResult)
                {
                    //若返回检测结果为200
                    if (200 === (int)$taskResult['code'])
                    {
                        //获取检测结果
                        $sceneResults = $taskResult['results'];
                        foreach ($sceneResults as $sceneResult)
                        {
                            $scene = (string)$sceneResult['scene'];//场景
                            $suggestion = (string)$sceneResult['suggestion'];//检测等级
                            $rate = $sceneResult['rate'];//比例
                            $label = $sceneResult['label'];//标签
                            $dataId = $taskResult['dataId'] ?? 0;//阿里数据id
                            //获取违规词信息
                            $text = [];
                            if (isset($sceneResult['details']))
                            {
                                foreach ($sceneResult['details'] as $detail)
                                {
                                    if (isset($detail['contexts']))
                                    {
                                        foreach ($detail['contexts'] as $context)
                                        {
                                            $text[] = $context['context'];
                                        }
                                    }
                                }
                            }
                            //若内容有违规且超过配置的相似比例，则进行隐藏不显示，将标签存入库中
                            if (($scene === 'antispam') && in_array($suggestion, self::$illegal_level, true) && $rate > $range)
                            {
                                $res['data_id'] = $dataId;
                                $res['label_type'] = $label;
                                $res['level'] = $suggestion;
                                $res['label'] = self::$illegal_label[$label];
                                $res['text'] = $text;
                                $res['filteredContent'] = $taskResult['filteredContent'] ?? '';
                            }
                        }
                    } else
                    {
                        Log::write('获取阿里文本内容检测结果失败：' . $taskResult['msg'] . '检测内容:' . $content, 'error', true);
                    }
                }
            } else
            {
                Log::write('请求阿里文本内容检测api失败：' . $response['msg'] . '检测内容:' . $content, 'error', true);
            }
        }
        catch (Exception $e)
        {
            Log::write('阿里文本内容检测失败：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】' . '检测内容:' . $content, 'error', true);
        }
        return $res;
    }

    /**
     * 图片违规检测
     * @document 接口文档 https://help.aliyun.com/document_detail/70292.html?spm=a2c4g.70430.0.0.25f1342bW6gBfq
     * @param bool $isApp 1=>不检测ORC
     * @param string $url
     * @param string $bizType
     * @param array|string[] $scenes ['porn/智能鉴黄', 'terrorism/暴恐涉政', 'ad/图文违规', 'live/不良场景', 'ocr/图文检测','qrcode/二维码','logo/图片logo']
     * @return array
     */
    public static function checkImg(bool $isApp, string $url, string $bizType = 'yellow_map', array $scenes = ['porn', 'terrorism', 'ad', 'live', 'qrcode', 'ocr'])
    {
        /**
         * 组合检测的数据
         */
        $contents[] = [
            'dataId' => uniqid('', false),//数据Id。需要保证在一次请求中所有的Id不重复。
            'url' => $url//待检测文本
        ];
        /**
         * 文本垃圾检测： antispam
         **/
        $param = [
            'tasks' => $contents,
            'scenes' => $scenes,
            'extras' => ['type' => 'advanced']//OCR高精度
        ];
        if ($bizType)
        {
            $param['bizType'] = $bizType;
        }
        $range = config('app.illegal_range');
        $client = AliyunClient::getInstance()->getClient();
        $request = new ImageSyncScanRequest();
        $request->setMethod('POST');
        $request->setAcceptFormat('JSON');
        $request->setContent(json_encode($param, true));
        $res = ['status' => true, 'text' => ''];
        try
        {
            //返回数据
            $response = $client->getAcsResponse($request);
            $response = json_decode(json_encode($response), true);
//            Log::write('图片检测返回数据:' . json_encode($response['data'], true), 'log', true);
            //若返回code为200
            if (200 === (int)$response['code'])
            {
                //检测结果信息
                $taskResults = $response['data'];
                foreach ($taskResults as $taskResult)
                {
                    //若返回检测结果为200
                    if (200 === (int)$taskResult['code'])
                    {
                        //获取检测结果
                        $sceneResults = $taskResult['results'];
                        foreach ($sceneResults as $sceneResult)
                        {
                            $label = $sceneResult['label'];//标签
                            $scene = (string)$sceneResult['scene'];//场景
                            $suggestion = (string)$sceneResult['suggestion'];//检测等级
                            $dataId = $taskResult['dataId'] ?? 0;//阿里数据id
                            $rate = $sceneResult['rate'] ?? 0;//比例
                            $labelText = self::$illegal_label[$sceneResult['label']] ?? '';
                            //若内容有违规且超过配置的相似比例，则进行隐藏不显示，将标签存入库中
                            if (in_array($suggestion, self::$illegal_level, true) && $rate > $range)
                            {
                                $res['data_id'] = $dataId;
                                $res['label_type'] = $label;
                                $res['level'] = $suggestion;
                                $res['text'] = $labelText;
                                $res['status'] = false;
                            }
                            //若内容有违规且超过配置的相似比例，则进行隐藏不显示，将标签存入库中
                            if (in_array($scene, $scenes, true))
                            {
                                //检测图片QCR
                                if ($scene === 'ocr' && !$isApp)
                                {
                                    if ($suggestion === 'pass')
                                    {
                                        Log::write('阿里图片检测不包含【之日】和【日期】：触发' . $scene . '场景,数据含有[' . $label . ']' . ' 图片地址：' . $url, 'error', true);
                                        $res['status'] = false;
                                        $res['text'] = $labelText;
                                        return $res;
                                    }
                                    $ocrData = implode(',', $sceneResult['ocrData']);
                                    //图片文字必须包含之日
                                    if (strpos($ocrData, '之日') === false)
                                    {
                                        Log::write('阿里图片检测不包含【之日】：触发' . $scene . '场景,数据含有[' . $label . ']' . ' 图片地址：' . $url, 'error', true);
                                        $res['status'] = false;
                                        $res['text'] = $labelText;
                                        return $res;
                                    }
                                    //图片文字必须包含当前年或前一年
                                    $nowYear = date('Y');
                                    $year = $nowYear - 1;
                                    if ((strpos($ocrData, $year) === false) && strpos($ocrData, $nowYear) === false)
                                    {
                                        Log::write("阿里图片检测不包含【{$year}和{$nowYear}】：触发" . $scene . '场景,数据含有[' . $label . ']' . ' 图片地址：' . $url, 'error', true);
                                        $res['status'] = false;
                                        $res['text'] = $labelText;
                                        return $res;
                                    }
                                }
                                if ($suggestion === 'block')
                                {
                                    //['porn/智能鉴黄', 'terrorism/暴恐涉政', 'ad/图文违规', 'live/不良场景', 'ocr/图文检测','qrcode/二维码','logo/图片logo']
                                    if ($sceneResult['scene'] === 'ad')
                                    {
                                        //检测出的问答图片有二维码的广告，过滤我们自己App里的截图或者功能页的图片（这里是防止星座卡片截图、星盘截图底部带的qr、）【日运分享的图没啥事】
                                        //问答提问照片
                                        //由于App内的二维码都是草料的，防止以后业务增多，再增加二维码，所以这里校验草料的域名，同时记录日志，如果存在其他恶意的图片，那么再改为草料的精确地址，比如：https://qr34.cn/FflOme
                                        if ($bizType === 'question_pic_check' && isset($sceneResult['qrcodeData']) && count($sceneResult['qrcodeData']) > 0 && strpos($sceneResult['qrcodeData'][0], 'qr34.cn'))
                                        {//包含草料的域名
                                            Log::write('阿里图片检测违规（但是这里通过了）：数据含有[' . $labelText . ']' . '，查看一下图片，如果不是App内的图片，需要修改此方法，' . ' 图片地址：' . $url, 'log', true);
                                            $res['status'] = true;
                                            return $res;
                                        }

                                        //问答，合盘分享的ocr检测要通过
                                        if (isset($sceneResults[5], $sceneResults[5]['scene'], $sceneResults[5]['ocrData']) && $bizType === 'question_pic_check' && $sceneResults[5]['scene'] === 'ocr' && count($sceneResults[5]['ocrData']) > 0 && strpos($sceneResults[5]['ocrData'][0], '长按扫码下载“准了”App'))
                                        {//ocr识别，包含"长按扫码下载“准了”App"，那么通过
                                            Log::write('阿里图片检测违规（但是这里通过了，因为包含"长按扫码下载“准了”App"）：数据含有[' . $labelText . ']' . '，查看一下图片，如果不是App内的图片，需要修改此方法，' . ' 图片地址：' . $url, 'log', true);
                                            $res['status'] = true;
                                            return $res;
                                        }
                                    }
                                    Log::write('阿里图片检测违规：数据含有[' . $labelText . ']' . ' 图片地址：' . $url, 'error', true);
                                    $res['status'] = false;
                                }
                            }
                        }
                    } else
                    {
                        Log::write('获取阿里图片检测结果失败：' . $taskResult['msg'] . '图片地址:' . $url, 'error', true);
                    }
                }
            } else
            {
                Log::write('请求阿里图片检测api失败：' . $response['msg'] . '图片地址:' . $url, 'error', true);
            }
        }
        catch (Exception $e)
        {
            Log::write('阿里图片检测失败：' . $e->getMessage() . '【' . $e->getFile() . $e->getLine() . '】' . '图片地址:' . $url, 'error', true);
        }
        return $res;
    }

    /**
     * 提交语音URL
     * @document https://help.aliyun.com/document_detail/89630.html?spm=a2c4g.11186623.6.682.60c23f55aI42qY
     * @param $url
     * @param string $bizType
     * @param array $scenes
     * @return array
     */
    public static function submitUrlSound($url, string $bizType = '', array $scenes = ['antispam'])
    {
        $param = [
            'tasks' => [
                'dataId' => uniqid('', false),
                'url' => $url,
            ],
            'scenes' => ['antispam'],
            'live' => false //true：表示语音流检测。false（默认）：表示音频文件检测。
        ];
        if ($bizType)
        {
            $param['bizType'] = $bizType;
        }
        $client = AliyunClient::getInstance()->getClient();
        $request = new VoiceAsyncScanRequest();
        $request->setMethod('POST');
        $request->setAcceptFormat('JSON');
        /**
         * 语音垃圾内容检测： antispam
         **/
        $request->setContent(json_encode($param));
        try
        {
            $response = $client->getAcsResponse($request);
            $response = json_decode(json_encode($response), true);
            if (200 === (int)$response['code'])
            {
                $taskResults = $response['data'];
                foreach ($taskResults as $taskResult)
                {
                    if (200 === $taskResult['data'])
                    {
                        $taskId = $taskResult['taskId'];
                        // 将taskId保存下来，间隔一段时间来轮询结果。
                        return [
                            'status' => true,
                            'task_id' => $taskId
                        ];
                    } else
                    {
                        Log::write('提交阿里语音检测失败：' . $taskResult['code'], 'error', true);
                    }
                }
            } else
            {
                Log::write('提交阿里语音检测api失败：' . $response['msg'], 'error', true);
            }
        }
        catch
        (Exception $e)
        {
            Log::write('提交阿里语音检测api失败：' . $e->getMessage() . "[文件信息:{$e->getFile()} - {$e->getLine()}]", 'error', true);
        }
        return [
            'status' => false,
            'task_id' => ''
        ];
    }

    /**
     * 提交语音本地文件
     * @document https://help.aliyun.com/document_detail/89630.html?spm=a2c4g.11186623.6.682.60c23f55aI42qY
     * @throws \OSS\Core\OssException
     */
    public static function submitLocalSound()
    {
        $client = AliyunClient::getInstance()->getClient();
        $request = new VoiceAsyncScanRequest();
        $request->setMethod('POST');
        $request->setAcceptFormat('JSON');
        $uploader = ClientUploader::getVoiceClientUploader($client);
        $url = $uploader->uploadFile('d:/暴恐涉政1.mp4');
// 如果是语音文件检测 type => file
        $task1 = [
            'dataId' => uniqid('', false),
            'url' => $url,
            'type' => 'file'
        ];

        /**
         * 语音垃圾内容检测： antispam
         **/
        $request->setContent(json_encode([
            'tasks' => [$task1],
            'scenes' => ['antispam']
        ]));
        try
        {
            $response = $client->getAcsResponse($request);
            print_r($response, null);
            if (200 == $response->code)
            {
                $taskResults = $response->data;
                foreach ($taskResults as $taskResult)
                {
                    if (200 == $taskResult['code'])
                    {
                        $taskId = $taskResult->taskId;
                        // 将taskId保存下来，间隔一段时间来轮询结果。
                        print_r($taskId, null);
                    } else
                    {
                        print_r("task process fail:" . $response->code, null);
                    }
                }
            } else
            {
                print_r('detect not success. code:' . $response->code, null);
            }
        }
        catch (Exception $e)
        {
            print_r($e, null);
        }
    }

    /**
     * 查询语音异步检测结果
     * @document https://help.aliyun.com/document_detail/89630.html?spm=a2c4g.11186623.6.682.60c23f55aI42qY
     * @param string $task_id
     * @return array|null
     */
    public static function checkSound(string $task_id = '')
    {
        $client = AliyunClient::getInstance()->getClient();
        if ($task_id !== '')
        {
            while (true)
            {
                $res = self::getTask($client, $task_id);
                if ($res['code'] === 280)
                {
                    sleep(10);
                } else if ($res['code'] === 200)
                {
//                    print_r('=====SUCCESS=====', null);
                    break;
                } else
                {
//                    print_r('=====ERROR=====', null);
                    break;
                }
            }
            return $res;
        }
    }

    /**
     * 获取语音异步检测结果
     * @param $client
     * @param $task_id
     * @return null
     */
    protected static function getTask($client, $task_id)
    {
        $request = new VoiceAsyncScanResultsRequest();
        $request->setMethod("POST");
        $request->setAcceptFormat("JSON");
        $request->setContent(json_encode([$task_id]));
        try
        {
            $response = $client->getAcsResponse($request);
            $response = json_decode(json_encode($response), true);
            if (200 === $response['code'])
            {
                $taskResults = $response['data'];
                foreach ($taskResults as $taskResult)
                {
                    if (200 === $taskResult['code'])
                    {
                        $sceneResults = $taskResult['results'];
                        foreach ($sceneResults as $sceneResult)
                        {
                            $suggestion = $sceneResult['suggestion'];
                            //若内容有违规且超过配置的相似比例，则进行隐藏不显示，将标签存入库中
                            if (in_array($suggestion, self::$illegal_level, true))
                            {
                                return [
                                    'status' => false,
                                    'code' => $taskResult['code'],
                                    'text' => self::$illegal_label[$sceneResult['label']],
                                ];
                            }
                        }
                    } else
                    {
                        Log::write('获取阿里语音检测结果失败：' . $taskResult['code'], 'error', true);
                    }
                    return [
                        'status' => true,
                        'code' => $taskResult['code'],
                        'text' => '',
                    ];
                }
            } else
            {
                Log::write('请求阿里语音检测api失败：' . $response['msg'], 'error', true);
                return [
                    'status' => true,
                    'code' => $response['code'],
                    'text' => '',
                ];
            }
        }
        catch (Exception $e)
        {
            Log::write('请求阿里语音检测api失败：' . $e->getMessage() . "[文件信息:{$e->getFile()} - {$e->getLine()}]", 'error', true);
        }
        return [
            'status' => true,
            'code' => 0,
            'text' => '',
        ];
    }
}
